import io from "socket.io-client";

export class SocketIO {
  constructor(url, accessToken) {
    this.connection = io( {
      path: url,
      transports: ['websocket'],
      query: {
        accessToken
      },
      autoConnect: false
    })
    this.connecting = null
    this.eventMap = new Map()
  }

  async connect() {
    if (this.connection.connected) {
      return Promise.resolve()
    }

    try {
      if (!this.connecting) {
        console.log('socketIO is connecting')
        this.connecting = this.ensureConnect()
        this.connecting.finally(() => {
          this.connecting = null
        })
      }

      await this.connecting
      return Promise.resolve()
    } catch (e) {
      return Promise.reject()
    }
  }

  connectedCallback() {
    const callback = this.eventMap.get('connect')
    if (callback && typeof callback === 'function') {
      callback()
    }
  }

  ensureConnect() {
    const promise = new Promise((resolve, reject) => {
      this.connection.on('connect', () => {
        console.log('socketIO connect success')
        this.connectedCallback()
        resolve(true)
      })

      this.connection.on('connect_error', error => {
        console.error('socketIO connect error', error)
        reject(false)
      })

      this.connection.on('connect_timeout', () => {
        console.error('socketIO connect timeout')
        reject(false)
      })
    })

    this.connection.connect()

    return promise
  }

  on(event, callback) {
    this.eventMap.set(event, callback)

    if (['connect', 'disconnect'].includes(event)) {
      return
    }

    this.connection.on(event, callback)
  }

  off(event) {
    this.eventMap.delete(event)

    if (['connect', 'disconnect'].includes(event)) {
      return
    }

    this.connection.off(event)
  }

  emit(event, data) {
    if (this.connection.connected) {
      this.connection.emit(event, data)
    }
  }

  disconnect() {
    return new Promise((resolve, reject) => {
      if (this.connection && this.connection.connected) {
        this.connection.on('disconnect', () => {
          console.log('socketIO disconnect')
          this.disconnectCallback()
          resolve(true)
        })

        const timer = setTimeout(() => {
          clearTimeout(timer)
          reject(false)
        }, 3000)
      } else {
        resolve(true)
      }
    })
  }

  disconnectCallback() {
    const callback = this.eventMap.get('disconnect')
    if (callback && typeof callback === 'function') {
      callback()
    }
  }

}
